/**
 * Copyright 2009-2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.model.elem;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import chunmap.model.algorithm.LinearReference;
import chunmap.model.coord.CoordinateSeq;
import chunmap.model.coord.Position;
import chunmap.model.geom.Geometry;
import chunmap.model.geom.LineString;
import chunmap.model.geom.GeoPoint;

/**
 * 线与线的交集结果，经常是既有点又有线。 点和线重叠时，删除点，保留线。
 * 
 * @author chunquedong
 * 
 */
public class PointLineBag {
	private final Set<GeoPoint> points = new HashSet<GeoPoint>();
	private final Set<LineString> lines = new HashSet<LineString>();

	/**
	 * 不考虑线线自相交的情况,不考虑线的拓扑相等。
	 * 
	 * @param g
	 */
	public void add(Geometry g) {
		if (g instanceof GeoPoint) {
			GeoPoint p = (GeoPoint) g;
			if (!points.contains(p)) {
				putPoint(p);
			}
		} else if (g instanceof LineString) {
			LineString line = (LineString) g;
			if (!lines.contains(line)) {
				putLine(line);
			}
		} else {
			throw new IllegalArgumentException(
					"noly supper point an lineString");
		}
	}

	public void add(PointLineBag bag) {
		for (GeoPoint p : bag.points) {
			this.add(p);
		}
		for (LineString l : bag.lines) {
			this.add(l);
		}
	}

	private void putPoint(GeoPoint p) {
		for (LineString line : lines) {
			if (line.onLineString(p.getCoordinate()))
				return;
		}
		points.add(p);
	}

	private void putLine(LineString line) {

		// 移除重叠的点
		GeoPoint tp = null;
		for (GeoPoint p : points) {
			if (line.onLineString(p.getCoordinate())) {
				tp = p;
				break;
			}
		}
		points.remove(tp);

		// 合并相连的线
		LineString tl = null;
		CoordSeqEditor newLine = new CoordSeqEditor(line.getPoints());
		for (LineString ls : lines) {
			CoordinateSeq l=ls.getPoints();
			if (newLine.canJoin(l)) {
				newLine.join(l);
				tl = ls;
				break;
			}
		}

		if (tl != null) {
			lines.remove(tl);
			lines.add(new LineString(newLine.toCoordinateSeq()));
		} else {
			lines.add(line);
		}
	}

	public int getPointSize() {
		return points.size();
	}

	public int getLineStringSize() {
		return lines.size();
	}

	public int size() {
		return points.size() + lines.size();
	}

	public boolean isEmpty() {
		if ((points.size() == 0) && (lines.size() == 0))
			return true;
		return false;
	}

	public List<Geometry> toList() {
		List<Geometry> geos = new ArrayList<Geometry>();
		for (GeoPoint p : points) {
			geos.add(p);
		}
		for (LineString l : lines) {
			geos.add(l);
		}
		return geos;
	}

	public List<GeoPoint> getPointList() {
		List<GeoPoint> pointList = new ArrayList<GeoPoint>();
		for (GeoPoint p : points) {
			pointList.add(p);
		}
		return pointList;
	}

	public List<LineString> getLineList() {
		List<LineString> lineList = new ArrayList<LineString>();
		for (LineString l : lines) {
			lineList.add(l);
		}
		return lineList;
	}
	
	public List<LineString> breakLine(LineString ls) {
		List<LineString> lines = new ArrayList<LineString>();
		lines.add(ls);

		List<GeoPoint> pointList = this.getPointList();
		for (GeoPoint p : pointList) {
			breakLine(p.getCoordinate(), lines);
		}

		List<LineString> lineList = this.getLineList();
		for (LineString l : lineList) {

			Position p1 = l.firstPoint();
			Position p2 = l.lastPoint();
			breakLine(p1, lines);
			breakLine(p2, lines);
		}

		return lines;
	}

	private void breakLine(Position p, List<LineString> lines) {
		for (int i = 0, n = lines.size(); i < n; i++) {
			LineString ls = lines.get(i);
			List<CoordinateSeq> newLines =LinearReference.splitLine(ls.getPoints(),p);
			if (newLines.size() == 2) {
				lines.remove(i);
				lines.add(new LineString(newLines.get(0)));
				lines.add(new LineString(newLines.get(1)));
				return;
			}
		}
	}

}
